//
//  UserManagerViewModel.swift
//  IdleFish
//
//  Created by Ben on 2021/10/26.
//

import SwiftUI
import Combine

// UserManager相关的网络请求协议
protocol UserManagerRequest {
    
    func viretyLoginName(nick_name: String) async throws -> NetworkBaseModel<NetworkNilModel>
    // 本期视频对登录功能的细节做一些补充,
    // 首先是login接口，返回的应该只是包含token信息的一个结构
    // 之后拿着这个token再去请求init信息，其中包含用户详细信息，以及服务器需要让APP知道的一些内容，比如新版本更新提示、活动banner等等
    // 这样设计的目的在于，当用户登录过并且没有退出的情况下，再次打开应用，可以直接请求init相关信息，把获取token和获取init信息拆分开
    
    // 接下来开始功能实现
    func login(nick_name: String, passWord: String) async throws -> NetworkBaseModel<LoginModel>
    
}

extension UserManagerRequest {
    
    func viretyLoginName(nick_name: String) async throws -> NetworkBaseModel<NetworkNilModel> {
        let result = try await NetworkTool.awaitRequest(request: API.check_register(nick_name: nick_name), responseType: NetworkNilModel.self)
        
        return result
    }
    
    func login(nick_name: String, passWord: String) async throws -> NetworkBaseModel<LoginModel> {
        let result = try await NetworkTool.awaitRequest(request: API.login(nick_name: nick_name, pass_word: passWord), responseType: LoginModel.self)
        
        return result
    }
    
}

// UserManagerViewModel遵循BenHUDAble协议，这样它就具备了显示提示框的功能
@MainActor
class UserManagerViewModel: ObservableObject, UserManagerRequest, BenHUDAble {
    
    static let shared: UserManagerViewModel = UserManagerViewModel()
    
    @AppStorage("token") var token: String = ""
    
    
    // 通过currFocusedField来触发View的刷新，在刷新的时候给LoginView的loginFocusedField设置正确的值，以获取对应焦点
    @Published var currFocusedField: LoginFocusField?
    
    @Published var presentLoginView: Bool = false
    
    // 用户登录的账号
    @Published var loginUserName: String = ""
    // 用来控制用户名输入框的删除按钮的显示和隐藏
    @Published var loginUserNameIsEmpty: Bool = true
    // 验证是否可以检查账号的注册状态
    @Published var canViretyUserNameRegisterStatus: Bool = false
    // 标记当前用户名（账号）是否已经注册过
    @Published var userNameIsRegisted: Bool = false
    
    // 记录密码
    @Published var loginPassWord: String = ""
    @Published var loginPassWordIsEmpty: Bool = true
    // 控制是否显示明文密码
    @Published var showUnSafePassWord: Bool = false
    
    // 验证是否可以检查账号的注册状态
    @Published var canLogin: Bool = false
    
    
    // 标记服务协议以及因袭权政策复选框的勾选状态
    @Published var agrementCheckBoxIsSelected: Bool = false
    
    private var cancellable: Set<AnyCancellable> = []
    init() {
        
        $loginUserName
            .dropFirst()
            .removeDuplicates()
            .sink { _loginUserName in
                Dlog(_loginUserName.count)
                
                withAnimation {
                    self.loginUserNameIsEmpty = _loginUserName.count == 0
                    self.canViretyUserNameRegisterStatus =  _loginUserName.count != 0
                }
            }
            .store(in: &cancellable)
        
        $userNameIsRegisted
            .dropFirst()
            .removeDuplicates()
            .sink { _checkUserNameRegisterStatus in
                // 验证用户名是否注册，需要有一个网络等待的提示
                Task {
                    await self.verifyUserNameIsRegistered()
                }
            }
            .store(in: &cancellable)
        
        
        // 监听loginPassWord的改变，在这里控制密码输入框清除按钮的显示与隐藏
        $loginPassWord
            .dropFirst()
            .removeDuplicates()
            .sink { _loginPassWord in
                Dlog(_loginPassWord.count)
                
                withAnimation {
                    // 密码字符串个数为0隐藏清除按钮，否则显示
                    self.loginPassWordIsEmpty = _loginPassWord.count == 0
                    self.canLogin = _loginPassWord.count != 0
                }
            }
            .store(in: &cancellable)
    }
    

    func verifyUserNameIsRegistered() async {
        
        self.wait(message: nil, gifImageName: "hud_wait.gif")
        
        do {
            let result = try await viretyLoginName(nick_name: loginUserName)
            self.hide()
            Dlog(result)
            
            self.currFocusedField = .passWord
            
        } catch {
            
            // 当网路请求返回错误的时候，需要弹出一个提示框，之前首页获取商品分类信息的网络请求，提示框是在View中进行控制的,因为View中持有BenHUDState这个ViewModel，那在其他的ViewModel中执行的网络请求，该如何控制BenHUDState去显示提示框？
            
            // 这里UP主给出一个自己想到的解决方案，当然这个解决方案应该不是最好的方式，如果有小伙伴有更好的思路，欢迎在视频下方留言或者私信UP主，十分感谢。
            
            // 开始完成这个需求
            Dlog(error)
            
            if let e = error as? APIError {
                switch e {
                case .custom(let msg):
                    self.show(message: msg, systemImageName: "xmark.seal.fill")
                }
            }
        }
    }
    
    func login() async {
        
        if agrementCheckBoxIsSelected == false {
            self.show(message: "请阅读并同意相关协议政策", systemImageName: nil)
            return
        }
        self.wait(message: nil, gifImageName: "hud_wait.gif")
        
        do {
            let result = try await login(nick_name: loginUserName, passWord: loginPassWord)
            
            // 现在再次请求登录
            // 接下来完成退出登录后在进入，请求init的功能
            self.token = result.data?.token ?? ""
            
            self.presentLoginView = false
            self.hide()

        } catch {
            
            if let e = error as? APIError {
                switch e {
                case .custom(let msg):
                    self.show(message: msg, systemImageName: "xmark.seal.fill")
                }
            }
        }
    }
    
}
